/*=============================================================================
#    The element-comm is a tentative program used to test our ideas.           
#                       Copyright (C)  bgao                                    
#
#     This program is free software; you can redistribute it and/or            
#      modify it under the terms of the GNU General Public License             
#     as published by the Free Software Foundation; either version 2           
#         of the License, or (at your option) any later version.               
#
#     This program is distributed in the hope that it will be useful,          
#    but WITHOUT ANY WARRANTY; without even the implied warranty of           
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            
#             GNU General Public License for more details.                    
#
#    You should have received a copy of the GNU General Public License        
#     along with this program; if not, write to the Free Software             
#      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,             
#                       MA  02110-1301, USA.                                  
#==============================================================================
# $Id: mac_gen.c 348 2010-11-29 09:01:16Z bgao $ 
#
# $Author: bgao $ 
#
# $Revision: 348 $ 
#
# $Date: 2010-11-29 17:01:16 +0800 (星期一, 29 十一月 2010) $ 
#
# Description: 
#
=============================================================================*/
/*! @file mac_gen.c
 *  @brief This module implements the MAC frame generator
 */
#include <string.h>
#include "mac.h"

/*! generate superframe.
 *  @param sup_frm pointer to structure of superframe.
 *  @return the generated 16-bits value of superframe.
 */
uint16 mac_gen_sup_frm(mac_sup_frm_t *sup_frm)
{
    uint16 s = 0;     /*!< palece to hold the binary string of the superframe */

    s |= (sup_frm->bcn_order      <<   MAC_BCN_ORDER_OFFSET    );
    s |= (sup_frm->sup_frm_order  <<   MAC_SUP_FRM_ORDER_OFFSET);
    s |= (sup_frm->fnl_cap_slot   <<   MAC_FNL_CAP_SLOT_OFFSET );
    s |= (sup_frm->ble            <<   MAC_BLE_OFFSET          );
    s |= (sup_frm->pan_coord      <<   MAC_PAN_COORD_OFFSET    );
    s |= (sup_frm->assoc_pmit     <<   MAC_ASSOC_PMIT_OFFSET   );

    return s;
}

/*! generate capability information.
 *  @param cap_info pointer to structure of capability information.
 *  @return the generated 16-bits value of capability information.
 */
uint8 mac_gen_cap_info(mac_cap_info_t *cap_info)
{
    uint16 c = 0;     /*!< place to hold the binary string of the capability information */

    c |= (cap_info->altnt_pan_coord   <<  ALTNT_PAN_COORD_OFFSET );
    c |= (cap_info->dev_type          <<  DEV_TYPE_OFFSET        );
    c |= (cap_info->pw_src            <<  PW_SRC_OFFSET          );
    c |= (cap_info->rec_on_when_idle  <<  REC_ON_WHEN_IDLE_OFFSET);
    c |= (cap_info->sec_cap           <<  SEC_CAP_OFFSET         );
    c |= (cap_info->alloc_addr        <<  ALLOC_ADDR_OFFSET      );

    return c;
}

/*! generate frame contrl filed in the mac header.
 * @param ctrl pointer to the structure of frame control field.
 * @return the generated 16-bits value frame control.
 */
uint16 mac_gen_frm_ctrl(mac_frm_ctrl_t *ctrl)
{
    uint16 f = 0;     /*!< place to hold the binary string of frame header */

    f |= (ctrl->frm_type        << MAC_FRM_TYPE_OFFSET);
    f |= (ctrl->en_sec          << MAC_EN_SEC_OFFSET);
    f |= (ctrl->frm_pend        << MAC_FRM_PEND_OFFSET);
    f |= (ctrl->ack             << MAC_ACK_OFFSET);
    f |= (ctrl->pan_id_comp     << MAC_PAN_ID_COMP_OFFSET);
    f |= (ctrl->dest_addr_m     << MAC_DEST_ADDR_M_OFFSET);
    f |= (ctrl->frm_ver         << MAC_FRM_VER_OFFSET);
    f |= (ctrl->src_addr_m      << MAC_SRC_ADDR_M_OFFSET);

    return f;
}

/*! return the length of the mac header.
 *  @param ctrl pointer to the structure of the frame control field of the mac header.
 *  @return the length of the mac header.
 */
static int8 calc_hdr_len(mac_frm_ctrl_t *ctrl)
{
    uint8 len = 0;

    len += 2;           /*!< 2 indicates the length of the frame control field */
    len += 1;           /*!< 1 indicates the length of the dsn */

    if (ctrl->dest_addr_m != NOT_PRESENT) {
        len += 2;
    }
    if (ctrl->pan_id_comp == 0 && ctrl->src_addr_m != NOT_PRESENT) {
        len += 2;
    }

    if (ctrl->dest_addr_m == ADDR_16_M) {
        len += 2;
    } else if (ctrl->dest_addr_m == ADDR_64_M) {
        len += 8;
    }

    if (ctrl->src_addr_m == ADDR_16_M) {
        len += 2;
    } else if (ctrl->src_addr_m == ADDR_64_M) {
        len += 8;
    }

    return len;
}

/*! generate the mac header.
 *  @param buf pointer to the buffer structure used to store the generated header.
 *  @param hdr pointer to the structure of the mac header.
 *  @return return -1 if error occurs else return 0.
 */
int8 mac_gen_hdr(buf_t *buf, mac_hdr_t *hdr)
{
    uint8 hdr_len = 0;
    mac_frm_ctrl_t *ctrl;
    uint16 ctrl_value;
    uint8 *p_uint8;
    uint16 *p_uint16;
    uint64 *p_uint64;

    if (buf == NULL || buf->ptr == NULL) {
        return MAC_INVALID_PARAMETER;
    }

    ctrl = &(hdr->ctrl);
    ctrl_value = mac_gen_frm_ctrl(ctrl);
    hdr_len = calc_hdr_len(ctrl);
    buf->length = buf->length + hdr_len + 1/* + 2*/;

    /*!< the buffer is used backward. */
    buf->ptr -= (hdr_len); /* left shift hdr_len bytes, got hdr_len+1 bytes. */

    /* start to copy each fields into the buffer. */
    /* length of the frame. */
    p_uint8 = (uint8 *)buf->ptr;
    /*!< plus the length field and the length of the MFR. */
    *p_uint8 = buf->length - 1 + 2; /* the length field does not include the length field itself */
    /*
    MAC_DEBUG("mac_gen_hdr: length of the buf is %d\n", buf->length);
    */
    buf->ptr++;

    /* frame control field. */
    p_uint16 = (uint16 *)buf->ptr;
    *p_uint16 = (uint16)ctrl_value;
    buf->ptr += 2;

    /* data sequence number. */
    p_uint8 =  (uint8 *)buf->ptr;
    *p_uint8 = (uint8)hdr->dsn;
    buf->ptr++;

    if (ctrl->dest_addr_m != NOT_PRESENT) {
        /* dest PAN ID. */
        p_uint16 = (uint16 *)buf->ptr;
        *p_uint16 = (uint16)(hdr->dest_pan_id);
        buf->ptr += 2;

        /* destination address. */
        if (ctrl->dest_addr_m == ADDR_64_M) {
            p_uint64 = (uint64 *)buf->ptr;
            *p_uint64 = hdr->dest_addr.a.addr64;
            /*
            MAC_DEBUG("mac_gen_hdr: dest_addr64 is 0x%x\n", *(uint32 *)buf->ptr);
            */
            buf->ptr += sizeof(uint64);
        } else if (ctrl->dest_addr_m == ADDR_16_M) {
            p_uint16 = (uint16 *)buf->ptr;
            *p_uint16 = hdr->dest_addr.a.addr16;
            buf->ptr += sizeof(uint16);
        }
    }

    if (ctrl->src_addr_m != NOT_PRESENT) {
        /* source PAN ID. */
        if (ctrl->pan_id_comp == 0) {
            p_uint16 = (uint16 *)buf->ptr;
            *p_uint16 = (uint16)(hdr->src_pan_id);
            /*
            MAC_DEBUG("\nmac_gen_hdr: src_pan_id is %d\n", *(uint16 *)buf->ptr);
            */
            buf->ptr += 2;
        }

        /* source address. */
        if (ctrl->src_addr_m == ADDR_64_M) {
            p_uint64 = (uint64 *)buf->ptr;
            *p_uint64 = hdr->src_addr.a.addr64;
            /*
            MAC_DEBUG("mac_gen_hdr: src_addr64 is %d\n", *(uint32 *)buf->ptr);
            */
            buf->ptr += sizeof(uint64);
        } else if (ctrl->src_addr_m == ADDR_16_M) {
            p_uint16 = (uint16 *)buf->ptr;
            *p_uint16 = hdr->src_addr.a.addr16;
            buf->ptr += sizeof(uint16);
        }
    }

    /* leave the MFR alone, it will be patched by radio. */

    /* back the buf pointer to the first valid byte. */
    /* 1 indicates the length of the frame length field. */
    buf->ptr -= (hdr_len + 1);

    return MAC_OK;
}

/*! generate the ack frame.
 *  @param buf pointer to the buffer structure used to store the frame.
 *  @param p pointer to the structure of mac ack frame.
 *  @return -1 if error occurs else return 0.
 */
int8 mac_gen_ack(buf_t *buf, mac_frm_ack_t *p)
{
    mac_hdr_t hdr;
    mac_frm_ctrl_t *ctrl = &hdr.ctrl;

    hdr.dsn = p->dsn;

    ctrl->frm_type     = FRM_TYPE_ACK;
    ctrl->en_sec       = 0;
    ctrl->ack          = 0;
    ctrl->frm_pend     = p->pend;
    ctrl->pan_id_comp  = 0;
    ctrl->dest_addr_m  = NOT_PRESENT;
    ctrl->frm_ver      = V_2006;
    ctrl->src_addr_m   = NOT_PRESENT;
    /*
    MAC_DEBUG("mac_gen_ack: dsn is %d\n", hdr.dsn);
    */

    return mac_gen_hdr(buf, &hdr);
}

/*! generate beacon frame.
 *  @param buf pointer to the structure used to store the generated frame.
 *  @param bcn pointer to the beacon structure.
 *  @return -1 if error occurs else return 0.
 *  TODO include the beacon payload.
 */
int8 mac_gen_bcn(buf_t *buf, mac_frm_bcn_t *bcn)
{
    mac_hdr_t hdr;
    mac_frm_ctrl_t *ctrl = &hdr.ctrl;
    uint16 *tmp;

    hdr.dsn = bcn->bsn;
    hdr.src_addr.mode = ADDR_16_M;
    hdr.src_addr.a.addr16 = bcn->src.a.addr16;
    hdr.src_pan_id = bcn->pan_id;
    /*
    MAC_DEBUG("mac_gen_bcn: hdr.src_pan_id is %d\n", hdr.src_pan_id);
    MAC_DEBUG("mac_gen_bcn: hdr.src_addr.a.addr16 is 0x%x\n", hdr.src_addr.a.addr16);
    */

    ctrl->frm_type     = FRM_TYPE_BCN;
    ctrl->en_sec       = 0;
    ctrl->ack          = 0;
    /*!< TODO check the dest addr to see if there are data for it. */
    ctrl->frm_pend     = 0;
    ctrl->pan_id_comp  = 0;
    ctrl->dest_addr_m  = NOT_PRESENT;
    ctrl->frm_ver      = V_2006;
    ctrl->src_addr_m   = ADDR_16_M;

    /* no bcn payload, no GTS, no pending address. */
    /*
    MAC_DEBUG("mac_gen_bcn: buf->length is %d\n", buf->length);
    */
    /* move pointer to the 16-bit */
    buf->ptr--;
    tmp = (uint16 *)buf->ptr;
    *tmp = bcn->sup_frm_value;
    /*
    MAC_DEBUG("mac_gen_bcn: sup_frm is 0x%x\n", *(uint16 *)buf->ptr);
    */

    buf->ptr--;
    buf->length += 2;
    /*
    MAC_DEBUG("mac_gen_bcn: sup_frm_value in buf is 0x%x\n", *(uint16 *)buf->ptr);
    */

    return mac_gen_hdr(buf, &hdr);
}

/*! generate association request frame, which is used by the association 
 *  request primitive.
 *  @param buf pointer to the structure used to store the generated frame.
 *  @param req pointer to the association request frame structure.
 *  @return -1 if error occurs else return 0.
 */
int8 mac_gen_assoc_req(buf_t *buf, mac_frm_assoc_req_t *req)
{
    uint8 *tmp;
    mac_hdr_t hdr;
    mac_frm_ctrl_t *ctrl = &hdr.ctrl;

    if (buf == NULL && buf->ptr == NULL && req == NULL) {
        return -1;
    }

    tmp = buf->ptr;
    *tmp = req->cap_info;
    /*
    MAC_DEBUG("mac_gen_assoc_req: req->cap_info is 0x%x\n", *(uint8 *)tmp);
    */
    buf->ptr--;
    buf->length++;

    tmp = buf->ptr;
    *tmp = CMD_ASSOC_REQ;
    /*
    MAC_DEBUG("mac_gen_assoc_req: cmd id is 0x%x\n", *(uint8 *)tmp);
    */
    buf->ptr--;
    buf->length++;

    ctrl->frm_type = FRM_TYPE_CMD;
    ctrl->en_sec = 0;
    ctrl->frm_pend = 0;
    ctrl->ack = 1;
    ctrl->pan_id_comp = 0;
    ctrl->dest_addr_m = req->dest.mode;
    ctrl->frm_ver = V_2006;
    ctrl->src_addr_m = req->src.mode;

    hdr.dsn = req->dsn;
    hdr.dest_pan_id = req->dest_pan_id;
    hdr.dest_addr.mode = req->dest.mode;
    /*! not know the mode, so copy the whole union, i.e. the 64-bits addr */
    hdr.dest_addr.a.addr64 = req->dest.a.addr64;
    hdr.src_pan_id = req->src_pan_id;
    hdr.src_addr.mode = ADDR_64_M;
    hdr.src_addr.a.addr64 = req->src.a.addr64;

    return mac_gen_hdr(buf, &hdr);
}

/*! generate association response frame, which is used by the association
 *  response primitive.
 *  @param buf pointer to the structure used to store the generated frame.
 *  @param resp pointer to the association response frame structure.
 *  @return -1 if error occurs else return 0.
 */
int8 mac_gen_assoc_resp(buf_t *buf, mac_frm_assoc_resp_t *resp)
{
    mac_hdr_t hdr;
    mac_frm_ctrl_t *ctrl = &hdr.ctrl;
    uint8 *p_uint8;
    uint16 *p_uint16;

    ctrl->frm_type = FRM_TYPE_CMD;
    ctrl->en_sec = 0;
    ctrl->frm_pend = 0;
    ctrl->ack = 1;
    ctrl->pan_id_comp = 1;
    ctrl->dest_addr_m = ADDR_64_M;
    ctrl->frm_ver = V_2006;
    ctrl->src_addr_m = ADDR_64_M;

    hdr.dsn = resp->dsn;
    hdr.dest_pan_id = resp->pan_id;
    hdr.dest_addr.mode = ADDR_64_M;
    hdr.dest_addr.a.addr64 = resp->dest.a.addr64;
    hdr.src_addr.mode = ADDR_64_M;
    hdr.src_addr.a.addr64 = resp->src.a.addr64;

    /* status field */
    p_uint8 = (uint8 *)buf->ptr;
    *p_uint8 = resp->status;
    buf->ptr--;
    buf->length++;

    /* short address field */
    buf->ptr--; /* move ptr to the low byte of the 16-bit value */
    p_uint16 = (uint16 *)buf->ptr;
    *p_uint16 = resp->short_addr;
    buf->ptr--;
    buf->length += 2;

    /* cmd id of assoc resp */
    p_uint8 = (uint8 *)buf->ptr;
    *p_uint8 = CMD_ASSOC_RESP;
    buf->ptr--;
    buf->length++;

    return mac_gen_hdr(buf, &hdr);
}

/*! generate the data request command frame, which is used by the
 *  data request primitive.
 *  @param buf pointer to the structure used to store the generated frame.
 *  @param req pointer to the data request frame.
 *  @return -1 if error occurs else return 0.
 */
int8 mac_gen_data_req(buf_t *buf, mac_frm_data_req_t *req)
{
    mac_hdr_t hdr;
    mac_frm_ctrl_t *ctrl = &hdr.ctrl;
    uint8 *p_uint8;

    ctrl->frm_type = FRM_TYPE_CMD;
    ctrl->en_sec = 0;
    ctrl->frm_pend = 0;
    ctrl->ack = 1;
    ctrl->pan_id_comp = req->pan_id_comp;
    ctrl->dest_addr_m = req->dest.mode;
    ctrl->frm_ver = V_2006;
    ctrl->src_addr_m = req->src.mode;

    hdr.dsn = req->dsn;
    hdr.dest_pan_id = req->dest_pan_id;
    hdr.dest_addr.mode = req->dest.mode;
    hdr.dest_addr.a.addr64 = req->dest.a.addr64;
    hdr.src_pan_id = req->dest_pan_id;
    hdr.src_addr.mode = req->src.mode;
    hdr.src_addr.a.addr64 = req->src.a.addr64;

    p_uint8 = buf->ptr;
    *p_uint8 = CMD_DATA_REQ;
    buf->ptr--;
    buf->length++;

    return mac_gen_hdr(buf, &hdr);
}

/*! generate beacon request frame, which is used in the process of active scan.
 *  @param buf pointer to the structure used to store the generated frame.
 *  @param dsn data sequence number.
 *  @return -1 if error occurs else return 0.
 */
int8 mac_gen_bcn_req(buf_t *buf, uint8 dsn)
{
    mac_hdr_t hdr;
    mac_frm_ctrl_t *ctrl = &hdr.ctrl;
    uint8 *p_uint8;

    ctrl->frm_type = FRM_TYPE_CMD;
    ctrl->en_sec = 0;
    ctrl->frm_pend = 0;
    ctrl->ack = 0;
    ctrl->pan_id_comp = 0;
    ctrl->dest_addr_m = ADDR_16_M;
    ctrl->frm_ver = V_2006;
    ctrl->src_addr_m = NOT_PRESENT;

    hdr.dsn = dsn;
    hdr.dest_pan_id = BROADCAST_PAN_ID;
    hdr.dest_addr.mode = ADDR_16_M;
    hdr.dest_addr.a.addr16 = BROADCAST_ADDR;

    p_uint8 = buf->ptr;
    *p_uint8 = CMD_BCN_REQ;
    buf->ptr--;
    buf->length++;

    return mac_gen_hdr(buf, &hdr);
}
